//	--------------------------------------------------------------------
//	Copyright(C) 2006,2007 Zhao Yukun. All rights reserved.
//	This file is a part of the Xeres system.
//	Xeres 2007
//	Contact : xeres.engine@gmail.com
//	$(reservedInfo)
//	$(reservedDeclaration)
//	--------------------------------------------------------------------
///	@file	<tls.cpp>
///	@path	~/xeres/src/lib/system/
///	@date	2007/12/08
///	@desc	Thread local storage.

#include "config/config.h"

#include "lib/system/tls.h"

namespace xeres {

	// get instance
	Tls& Tls::instance( void )
	{
		static Tls s_tlsInstance;
		return s_tlsInstance;
	}

	// ctor
	Tls::Tls( void )
	{
	}

	// dtor
	Tls::~Tls( void )
	{
		// delete all missed space
		ThreadMap::iterator it = m_threadInfo.begin();
		while( it != m_threadInfo.end() )
		{
			_TlsThreadInfo * info = it->second;
			CleanupThread( info );
			delete it->second;
			++it;
		}
	}

	// allocate new tls slot
	int Tls::Alloc( size_t size , function<void(void*,TLS_MANAGE)> manage_call )
	{
		// lock
		LockGuard<ThreadLock> s_locker( m_lock );

		// make record
		int id = (int)m_slots.size();

		_TlsSlot slot;
		slot.m_size = size;
		slot.m_handler = manage_call;
		m_slots.push_back( slot );

		return id;
	}

	void * Tls::Acquire( int index )
	{
		if( index < 0 || index >= (int)m_slots.size() )
		{
			TRACE_ERROR( _S("Tls::Acquire: Invalid TLS index.\n") );
			return NULL;
		}

		// lock
		LockGuard<ThreadLock> s_locker( m_lock );

		// find index & return
		_TlsSlot& slot = m_slots[index];

		return AllocSpace( slot );
	}

	void Tls::OnThreadExit( void )
	{
		// lock
		LockGuard<ThreadLock> s_locker( m_lock );
		_TlsThreadInfo * info = NULL;
		DWORD thread_id = ::GetCurrentThreadId();
		ThreadMap::iterator it = m_threadInfo.find( thread_id );
		if( it != m_threadInfo.end() )
		{
			_TlsThreadInfo * info = it->second;
			CleanupThread( info );
			delete it->second;
		}
	}

	void Tls::CleanupThread( _TlsThreadInfo * info )
	{
		if( info->m_thread )
		{
			// !! managed thread has non-free TLS,
			// !! might be a terminated thread
			TRACE_ERROR( _S("Tls::DTOR: Managed thread has non-free TLS.") );
			// free them
			for( size_t i = 0 , total = info->m_spaces.size() ; i < total ; ++i )
			{
				_TlsSpace& space = info->m_spaces[i];

				// execute cleanup
				try
				{
					space.m_slot->m_handler( space.m_space , TLS_MANAGE_FREE_AT_EXIT );
					free( space.m_space );
				}
				catch (...)
				{
					// skip them while system shut down
				}
			}
		}
		else
		{
			// unmanaged thread
			TRACE_WARNING( _S("Tls::DTOR: Used TLS in unmanaged thread.") );
			// free them
			for( size_t i = 0 , total = info->m_spaces.size() ; i < total ; ++i )
			{
				_TlsSpace& space = info->m_spaces[i];

				// execute cleanup
				try
				{
					space.m_slot->m_handler( space.m_space , TLS_MANAGE_FREE_AT_EXIT );
					free( space.m_space );
				}
				catch (...)
				{
					// skip them while system shut down
				}
			}
		}
	}

	void * Tls::AllocSpace( _TlsSlot& slot )
	{
		// allocate new space
		void * ptr = malloc( slot.m_size );
		// perform initialize
		slot.m_handler( ptr , TLS_MANAGE_ALLOC_LOCAL );
		// add record
		_TlsThreadInfo * info = NULL;
		DWORD thread_id = ::GetCurrentThreadId();
		ThreadMap::iterator it = m_threadInfo.find( thread_id );
		if( it == m_threadInfo.end() )
		{
			// add new thread info
			info = new _TlsThreadInfo;
			info->m_thread = Thread::GetCurrent();
			if( info->m_thread == NULL )
			{
				TRACE_WARNING( _S("Tls::AllocSpace: Use TLS in unmanaged thread.") );
			}
			m_threadInfo[thread_id] = info;
		}
		else
		{
			info = it->second;
		}
		_TlsSpace space;
		space.m_slot = &slot;
		space.m_space = ptr;
		info->m_spaces.push_back( space );
		return ptr;
	}

} // namespace xeres
